﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Specialized;
using System.Reflection;

namespace LimnorDesigner
{
    public class CustomTypeAttribute : Attribute
    {
        public CustomTypeAttribute()
        {
        }
        public CustomTypeAttribute(int classId)
        {
            ClassId = classId;
        }
        public Type DataType { get; set; }
        public int ClassId { get; set; }
    }
    public class ClassHolder:IComponent
    {
        public ClassHolder()
        {
        }
        //[CustomTypeAttribute]
        //public int DataType { get; set; }
        static Dictionary<int, Type> _dynamicTypes;
        public static Type CreateType(int classId, string name, Type t)
        {
            Type retType = null;
            if (_dynamicTypes == null)
            {
                _dynamicTypes = new Dictionary<int, Type>();
            }
            if (_dynamicTypes.TryGetValue(classId, out retType))
            {
                if (retType.Name != name)
                {
                    _dynamicTypes.Remove(classId);
                }
                else
                {
                    return retType;
                }
            }
            string nsName = "LimnorStudioDesigner";
            // Create a code compile unit and a namespace
            CodeCompileUnit ccu = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace(nsName);
            //
            // Add some imports statements to the namespace
            ns.Imports.Add(new CodeNamespaceImport("System"));
            ns.Imports.Add(new CodeNamespaceImport("LimnorDesigner"));
            //
            // Add the namespace to the code compile unit
            ccu.Namespaces.Add(ns);
            //
            //it can be sure that name is unique because it is a component name
            CodeTypeDeclaration ctd = new CodeTypeDeclaration(name);
            ctd.BaseTypes.Add(typeof(ClassHolder));
            ns.Types.Add(ctd);
            //
            ctd.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(CustomTypeAttribute)),
                new CodeAttributeArgument(new CodePrimitiveExpression(classId))
                //,new CodeAttributeArgument(new CodeTypeReferenceExpression(t))
                )
                );
            //
            Dictionary<string, Assembly> _refLocations = new Dictionary<string, Assembly>();
            DesignUtil.FindReferenceLocations(_refLocations, typeof(ClassHolder).Assembly);
            //
            //compile it
            CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
            CompilerParameters cp = new CompilerParameters(new string[] { 
                //"System.dll"
                //,typeof(ClassHolder).Assembly.Location
                //,typeof(VPL.VPLUtil).Assembly.Location 
            });
            Assembly aly = Assembly.GetAssembly(typeof(System.ComponentModel.Component));
            if (!_refLocations.ContainsKey(aly.Location.ToLower()))
            {
                cp.ReferencedAssemblies.Add(aly.Location);
            }
            foreach (string loc in _refLocations.Keys)
            {
                cp.ReferencedAssemblies.Add(loc);
            }
            cp.GenerateInMemory = true;
            cp.OutputAssembly = "LimnorStudioAutoGenerated.dll";
            cp.GenerateExecutable = false;
            cp.CompilerOptions = "/t:library";
            CompilerResults results = provider.CompileAssemblyFromDom(cp, ccu);
            if (results.Errors == null || results.Errors.Count == 0)
            {
                Type[] tps = results.CompiledAssembly.GetExportedTypes();
                for (int i = 0; i < tps.Length; i++)
                {
                    if (tps[i].Name == name)
                    {
                        retType = tps[i];
                        _dynamicTypes.Add(classId, retType);
                        break;
                    }
                }
            }
            else
            {
                StringCollection sc = new StringCollection();
                foreach(CompilerError e in results.Errors)
                {
                    sc.Add(e.ErrorText);
                }
                FormMsgList.ShowStrings(sc);
            }
            return retType;
        }

        [Description("Name of the component")]
        [ParenthesizePropertyName(true)]
        public string Name
        {
            get
            {
                if (Site != null)
                {
                    if (!string.IsNullOrEmpty(Site.Name))
                    {
                        return Site.Name;
                    }
                }
                if (!string.IsNullOrEmpty(_name))
                    return _name;
                return "";
            }
            set
            {
                if (Site != null)
                    Site.Name = value;
                _name = value;
            }
        }
        #region IComponent Members

        public event EventHandler Disposed;
        private ISite _site;
        private string _name;
        [ReadOnly(true)]
        [Browsable(false)]
        public ISite Site
        {
            get
            {
                return _site;
            }
            set
            {
                _site = value;
                if (_site != null)
                {
                    if (string.IsNullOrEmpty(_site.Name))
                    {
                        if (!string.IsNullOrEmpty(_name))
                        {
                            _site.Name = _name;
                        }
                    }
                    else
                    {
                        _name = _site.Name;
                    }
                }
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (Disposed != null)
            {
                Disposed(this, new EventArgs());
            }
        }

        #endregion
    }
}
